package service

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/shopspring/decimal"
	"io/ioutil"
	"log"
	"mojieshi/define"
	"mojieshi/models"
	"mojieshi/tools"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// 微信小程序apiv3支付
type OrderPayJson struct {
	Payorderid int    `json:"payorderid"` //订单支付表id
	Orderid    int    `json:"orderid"`
	Ordernum   string `json:"ordernum"`
}

// 微信支付回调
type mallLogic struct {
}

var MallLogic mallLogic

type name struct {
}

func PayWXMiniCallback(c *gin.Context) {
	FAIL := gin.H{
		"return_code": "FAIL",
		"return_msg":  "OK",
	}
	SUCCESS := gin.H{
		"return_code": "SUCCESS",
		"return_msg":  "OK",
	}

	body, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		panic(err)
	}
	var notification models.PayCallbackNotification
	err = xml.Unmarshal(body, &notification)
	if err != nil {
		c.XML(http.StatusOK, FAIL)
		return
	}

	if notification.ResultCode == "SUCCESS" {
		logic := MallLogic
		err = logic.PayMiniCallBack(notification)
		if err != nil {
			c.XML(http.StatusOK, FAIL)
			return
		}
		c.XML(http.StatusOK, SUCCESS)
		return
	} else {
		c.XML(http.StatusOK, FAIL)
		return
	}
}

//微信统一下单

type AppCreateOrderResult struct {
	AppID     string `xml:"appid" json:"appid"`
	Package   string `xml:"package" json:"package"`
	NonceStr  string `xml:"noncestr" json:"noncestr"`
	Timestamp string `xml:"timestamp" json:"timestamp"`
	Sign      string `xml:"sign" json:"sign"`
}

func PayOrderByWxMini(c *gin.Context) {
	var jsonData OrderPayJson
	err := c.ShouldBindJSON(&jsonData)
	if err != nil {
		define.Response.ResultFail(c, 10000, define.JsonDataX, "")
		return
	}
	//获取支付表信息
	var orderInfo models.MPayOrder
	err = models.DB.Model(&orderInfo).Where("id=?", jsonData.Payorderid).First(&orderInfo).Error
	if err != nil {
		define.Response.ResultFail(c, 10001, err.Error(), "")
		return
	}
	//获取当前时间戳，计算超超时
	now := time.Now().Unix()
	Cnow := int64(orderInfo.Createtime) + int64(60*15)
	yyyy := time.Unix(Cnow, 0).Format("20060102150405")
	//判断支付过期时间
	if now > Cnow {
		define.Response.ResultFail(c, 10002, "支付超时", "")
		return
	}
	//	判断支付状态
	if orderInfo.Status != 0 {
		define.Response.ResultFail(c, 10003, "该订单已无法支付", "")
		return
	}
	if orderInfo.TradeNo != "" {
		var result *AppCreateOrderResult
		err = json.Unmarshal([]byte(orderInfo.CreateOrderSign), &result)
		if err != nil {
			define.Response.ResultFail(c, 10004, err.Error(), "")
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"success": true,
			"code":    0,
			"data":    result,
		})
	} else {
		var wXUserInfo models.MMember
		models.DB.Model(&wXUserInfo).Where("id=?", orderInfo.Mid).First(&wXUserInfo)
		if wXUserInfo.Wxopenid == "" {
			define.Response.ResultFail(c, 10005, "用户不存在", "")
			return
		}
		//获取ip
		ip := c.ClientIP()
		//获取支付设置
		var paySetData models.MPaySet
		err = models.DB.Model(new(models.MPaySet)).Where("id=?", orderInfo.Paytypeid).First(&paySetData).Error
		if err != nil {
			define.Response.ResultFail(c, 10006, define.JsonDataX, "")
			return
		}
		NotifyUrl := define.WebUrl + paySetData.NotifyUrl
		param := CreateOrderParam{
			NonceStr:   tools.Token(),
			Body:       orderInfo.Title,
			OutTradeNo: orderInfo.Ordernum,
			IP:         ip,
			TimeExpire: yyyy,
			TotalFee:   uint(orderInfo.Money.Mul(decimal.NewFromInt(100)).IntPart()), // 微信必须转化成分做单位
			Openid:     wXUserInfo.Wxopenid,
		}
		(&param).GenerateSignResult(paySetData.AppId, paySetData.MerchantId, NotifyUrl, paySetData.AppSecret)

		result, err := RequestCreateOrder(&param)
		if err != nil {
			define.Response.ResultFail(c, 10007, define.JsonDataX, "")
			return
		}
		appResult := (&CreateOrderResult{}).GetSignResult(result, paySetData.AppId, paySetData.AppSecret)
		js, err := json.Marshal(&appResult)
		if err != nil {
			define.Response.ResultFail(c, 10008, define.JsonDataX, "")
			return
		}
		var detail = map[string]interface{}{
			"create_order_sign": string(js),
		}
		err = models.DB.Model(new(models.MPayOrder)).Where("id=?", jsonData.Payorderid).Updates(&detail).Error
		if err != nil {
			define.Response.ResultFail(c, 10009, define.JsonDataX, "")
			return
		}
		c.JSON(http.StatusOK, gin.H{
			"success": true,
			"code":    0,
			"data":    appResult,
		})
	}
}

type CreateOrderResult struct {
	XMLName    xml.Name `xml:"xml"`
	ReturnCode string   `xml:"return_code" json:"return_code"`
	ResultCode string   `xml:"result_code" json:"result_code"`
	ReturnMsg  string   `xml:"return_msg" json:"return_msg"`
	AppID      string   `xml:"appid" json:"appid"`
	MchId      string   `xml:"mch_id" json:"mch_id"`
	NonceStr   string   `xml:"nonce_str" json:"nonce_str"`
	Sign       string   `xml:"sign" json:"sign"`
	PrepayID   string   `xml:"prepay_id" json:"prepay_id"`
	TradeType  string   `xml:"trade_type" json:"trade_type"`
	ErrCodeDes string   `xml:"err_code_des" json:"err_code_des"`
}
type AppCreateOrderResultXCX struct {
	AppID     string `xml:"appid" json:"appid"`
	Package   string `xml:"package" json:"package"`
	NonceStr  string `xml:"noncestr" json:"noncestr"`
	Timestamp string `xml:"timestamp" json:"timestamp"`
	Sign      string `xml:"sign" json:"sign"`
}

func (*CreateOrderResult) GetSignResult(d *CreateOrderResult, MiniAppID string, APIKey string) *AppCreateOrderResultXCX {
	timestamp := strconv.FormatInt(time.Now().Unix(), 10)

	signStr := "appId=" + MiniAppID
	signStr = signStr + "&" + "nonceStr=" + d.NonceStr
	signStr = signStr + "&" + "package=" + "prepay_id=" + d.PrepayID
	signStr = signStr + "&" + "signType=MD5"
	signStr = signStr + "&" + "timeStamp=" + timestamp
	signStr = signStr + "&key=" + APIKey

	r := &AppCreateOrderResultXCX{
		AppID:     MiniAppID,
		Package:   "prepay_id=" + d.PrepayID,
		NonceStr:  d.NonceStr,
		Timestamp: timestamp,
		Sign:      strings.ToUpper(define.MD5(signStr)),
	}
	return r
}
func RequestCreateOrder(param *CreateOrderParam) (*CreateOrderResult, error) {
	xmlStr, err := xml.MarshalIndent(&param, "", "\t")
	if err != nil {
		panic(err)
	}

	client := &http.Client{}
	request, err := http.NewRequest("POST", define.CreateOrderURL, strings.NewReader(string(xmlStr)))
	if err != nil {
		panic(err)
	}

	request.Header.Set("Content-Type", "application/xml; charset=UTF-8")
	response, err := client.Do(request)
	if err != nil {
		panic(err)
	}

	defer func() { _ = response.Body.Close() }()
	result, err := ioutil.ReadAll(response.Body)
	if err != nil {
		panic(err)
	}

	log.Println(string(result))

	var r CreateOrderResult
	err = xml.Unmarshal(result, &r)
	if err != nil {
		panic(err)
	}

	if r.IsOK() == false {
		log.Println(string(xmlStr))
		return nil, errors.New(r.ErrCodeDes)
	}

	return &r, nil
}
func (r *CreateOrderResult) IsOK() bool {
	return r.ReturnCode == "SUCCESS" && r.ResultCode == "SUCCESS"
}
func generateOrderWxH5PayOutTradeNo(userId uint, orderId uint) string {
	return combineTradeNo(userId, "M5", orderId)
}
func generateOrderWxPayMiniOutTradeNo(userId uint, orderId uint) string {
	return combineTradeNo(userId, "M4", orderId)
}
func combineTradeNo(userId uint, flag string, orderId uint) string {
	vn := "TN"
	return fmt.Sprintf("%s%dT%s%s%d", vn, userId, time.Now().Format("060102150405"), flag, orderId)
}

// 统一下单参数
type CreateOrderParam struct {
	XMLName    xml.Name `xml:"xml"`
	AppID      string   `xml:"appid"`
	MchId      string   `xml:"mch_id"`    // 商户号
	NonceStr   string   `xml:"nonce_str"` // 随机字符串
	Body       string   `xml:"body"`      // 商品描述
	Openid     string   `xml:"openid"`
	OutTradeNo string   `xml:"out_trade_no"`
	IP         string   `xml:"spbill_create_ip"` // 终端IP
	TimeExpire string   `xml:"time_expire"`      // 交易结束时间 订单失效时间，格式为yyyyMMddHHmmss， 如2009年12月27日9点10分10秒表示为20091227091010。
	TotalFee   uint     `xml:"total_fee"`
	TradeType  string   `xml:"trade_type"`
	NotifyUrl  string   `xml:"notify_url"` // 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
	Sign       string   `xml:"sign"`
}

func (c *CreateOrderParam) GenerateSignResult(MiniAppID string, MchID string, notifyUrl string, APIKey string) {
	c.Init(MiniAppID, MchID, notifyUrl)
	signStr := "appid=" + MiniAppID
	signStr = signStr + "&" + "body=" + c.Body
	signStr = signStr + "&" + "mch_id=" + MchID
	signStr = signStr + "&" + "nonce_str=" + c.NonceStr
	signStr = signStr + "&" + "notify_url=" + notifyUrl
	signStr = signStr + "&" + "openid=" + c.Openid
	signStr = signStr + "&" + "out_trade_no=" + c.OutTradeNo
	signStr = signStr + "&" + "spbill_create_ip=" + c.IP
	signStr = signStr + "&" + "time_expire=" + c.TimeExpire
	signStr = signStr + "&" + "total_fee=" + fmt.Sprintf("%d", c.TotalFee)
	signStr = signStr + "&" + "trade_type=JSAPI"
	signStr = signStr + "&key=" + APIKey
	c.Sign = strings.ToUpper(define.MD5(signStr))
}
func (c *CreateOrderParam) Init(MiniAppID string, MchID string, notifyUrl string) {
	c.AppID = MiniAppID
	c.MchId = MchID
	c.NotifyUrl = notifyUrl
	c.TradeType = "JSAPI"
}
